// Main Application
// (c) jimon game studio

#include "Config_Engine.h"

#include <iostream>
#ifdef JE_MSVC
#include <conio.h>
#endif
#include <math.h>
#include <fstream>

#define JE_CONSOLE
#define JE_STATIC_GAME

#include "jeEngineController.h"
#include "ILogManager.h"
#include "IVariables.h"

#ifdef JE_STATIC_GAME
#include "IGameApplication.h"
#endif

#ifndef JE_STATIC_GAME
#include <windows.h>
#endif

#ifndef JE_CONSOLE
#include <windows.h>
#endif

#ifdef JE_STATIC_GAME
#ifdef JE_THREADS
#include <omp.h>
#endif
#endif

#include <string>

using namespace je;

class ApplicationLauncher
{
private:
	je::jeStringc ApplicationDllName;
	je::jeStringc ApplicationCreateProcedureName;
public:

	ApplicationLauncher(std::string Params)
	{
		ApplicationDllName = "Game.Dll";
		ApplicationCreateProcedureName = "CreateGameApplication";

		ParseArguments("-Vars32 Window_Width=800 -Vars32 Window_Height=600 -Vars8 Window_VSync=0 -Vars8 Window_VSync=0 -Vars8 Window_Depth=32 -Vars8 Window_Fullscreen=0 -Vars8 Window_Hertz=0 -Vars8 Window_MSAA=0");

		ParseOptions();
		ParseArguments(Params);
	}

	~ApplicationLauncher()
	{
	}

	void ParseOptions()
	{
		std::ifstream Options("Launcher.ini");

		if(!Options.is_open())
			return;

		enum ParameterType
		{
			PT_S8,
			PT_S32,
			PT_STRING
		};

		ParameterType Type = PT_STRING;

		std::string Params = "";

		while(!Options.eof())
		{
			std::string temp;
			Options>>temp;

			if(temp == "[s32]")
			{
				Type = PT_S32;
				continue;
			}

			if(temp == "[s8]")
			{
				Type = PT_S8;
				continue;
			}

			if(temp == "[string]")
			{
				Type = PT_STRING;
				continue;
			}

			switch(Type)
			{
			case PT_S8:
				Params += "-Vars8 ";
				break;
			case PT_S32:
				Params += "-Vars32 ";
				break;
			case PT_STRING:
				Params += "-Varstring ";
				break;
			}
			Params += temp;
			Params += " ";
		}

		ParseArguments(Params);
	}

	void ParseArguments(std::string Arguments)
	{
		using namespace je;

		jeStringc Parse = Arguments.c_str();

		enum ArgPar
		{
			AP_NONE,
			AP_DLLNAME,
			AP_CREATEPROCEDURENAME,
			AP_VARSETS8,
			AP_VARSETS32,
			AP_VARSETSTRING,
		};

		u32 LastPos = 0;
		ArgPar LastArgPar = AP_NONE;
		for(u32 i=0;i<=Parse.GetSize();i++)
		{
			if(Parse[i] == ' ')
			{
				jeStringc Arg = Parse.SubString(LastPos,i-LastPos);
				LastPos = i+1;

				if(!Arg.GetSize())
					continue;

				if(LastArgPar == AP_NONE)
				{
					if(Arg == jeStringc("-App"))
						LastArgPar = AP_DLLNAME;

					if(Arg == jeStringc("-AppProc"))
						LastArgPar = AP_CREATEPROCEDURENAME;

					if(Arg == jeStringc("-Vars8"))
						LastArgPar = AP_VARSETS8;

					if(Arg == jeStringc("-Vars32"))
						LastArgPar = AP_VARSETS32;

					if(Arg == jeStringc("-Varstring"))
						LastArgPar = AP_VARSETSTRING;

				}
				else
				{
					jeStringc ArgVarName;
					jeStringc ArgVarSet;
					if(LastArgPar >= AP_VARSETS8)
					{
						u1 Finded = false;
						u32 Find = Arg.Find('=',Finded);
						ArgVarName = Arg.SubString(0,Find);
						ArgVarSet = Arg.SubString(Find+1,Arg.GetSize()-Find-1);
					}

					switch(LastArgPar)
					{
					case AP_DLLNAME:
						ApplicationDllName = Arg;
						break;
					case AP_CREATEPROCEDURENAME:
						ApplicationCreateProcedureName = Arg;
						break;
					case AP_VARSETS8:
						core::GetVariables()->SetVariable(ArgVarName,s8(atoi(ArgVarSet.c_str())));
						break;
					case AP_VARSETS32:
						core::GetVariables()->SetVariable(ArgVarName,s32(atoi(ArgVarSet.c_str())));
						break;
					case AP_VARSETSTRING:
						core::GetVariables()->SetVariable(ArgVarName,jeStringd(ArgVarSet.c_str()));
						break;
					}

					LastArgPar = AP_NONE;
				}
			}
		}
	}


	void LaunchApplication()
	{
		using namespace je;

		app::IApplication * Application = NULL;

		jeStringc ApplicationDllName = "Game.Dll";
		jeStringc ApplicationCreateProcedureName = "CreateGameApplication";


		#ifdef JE_STATIC_GAME
			Application = app::CreateGameApplication();
		#else
			HMODULE hGameModule = LoadLibrary(jeStringd(ApplicationDllName.c_str()).c_str());
			if(hGameModule == NULL)
				JELOG_E("Cant Load Application Lib")
			else
			{
				typedef app::IApplication* (*CreateApplication)();
				CreateApplication CreateApp = (CreateApplication)GetProcAddress(hGameModule,ApplicationCreateProcedureName.c_str());
				if (CreateApp == NULL)
					JELOG_E("Cant Find Create Application Function")
				else
					Application = (CreateApp)();
			}
		#endif

		if(Application)
		{
			core::RunEngine(Application);
			JEDELETE(Application)
		}

		#ifndef JE_STATIC_GAME
			if(hGameModule)
				FreeLibrary(hGameModule);
		#endif
	}

};


#ifdef JE_CONSOLE
#ifdef JE_MSVC
int CrtReportHook(int nRptType, char *szMsg, int *retVal)
{
	printf("%s",szMsg);
	return 0;
}
#endif
#endif

#ifdef JE_CONSOLE
int main(int argc, char* argv[])
#else
int __stdcall WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
#endif
{
	#ifdef JE_MSVC
	_set_SSE2_enable(1);
	#ifdef JE_CONSOLE
	_CrtSetReportHook (CrtReportHook);
	_CrtMemState s1, s2, s3;
	_CrtMemCheckpoint(&s1);
	#endif
	#endif

	std::string * Params = new std::string;
	#ifdef JE_CONSOLE
		for(int i=1;i<argc;i++)
		{
			*Params += argv[i];
			*Params += " ";
		}
	#else
		*Params += lpCmdLine;
		*Params += " ";
	#endif

	if(Params->size() == 1)
		*Params = "";

	je::core::InitEngine();

	ApplicationLauncher * Launcher = new ApplicationLauncher(*Params);
	Launcher->LaunchApplication();
	delete Launcher;

	je::core::DeInitEngine();

	delete Params;

	#ifdef JE_CONSOLE

	#ifdef JE_MSVC
	_CrtMemCheckpoint(&s2);
	if(_CrtMemDifference(&s3, &s1, &s2))
	{
		_CrtMemDumpStatistics(&s3);
		_CrtDumpMemoryLeaks();
	}
	#endif

	//#ifdef JE_MSVC
	//printf("Press any key to continue\n");
	//_getch();
	//#endif

	#endif

	return 0;
}
